﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace LonCanvas
{
    public partial class CanvaControl : UserControl, ICanvas
    {
        public CanvaControl()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            m_cursors.AddCursor(eCommandType.select, Cursors.Arrow);
            m_cursors.AddCursor(eCommandType.draw, Cursors.Cross);
            m_cursors.AddCursor(eCommandType.pan, "hmove.cur");
            m_cursors.AddCursor(eCommandType.move, Cursors.SizeAll);
            m_cursors.AddCursor(eCommandType.edit, Cursors.Cross);
            UpdateCursor();

        }
        CursorCollection m_cursors = new CursorCollection();
        eCommandType m_commandType = eCommandType.select;
        private float zoom = 1;
        bool m_runningSnaps = true;
        Type[] m_runningSnapTypes = null;
        PointF m_mousedownPoint;
        IDrawObject m_newObject = null;
        IEditTool m_editTool = null;
        SelectionRectangle m_selection = null;
        string m_drawObjectId = string.Empty;
        string m_editToolId = string.Empty;
        Bitmap m_staticImage = null;
        bool m_staticDirty = true;
        ISnapPoint m_snappoint = null;

        UnitPoint m_lastCenterPoint;
        PointF m_panOffset = new PointF(0, 0);
        PointF m_dragOffset = new PointF(0, 0);
        float m_screenResolution = 20;
        IModel m_model;
        public Type[] RunningSnaps
        {
            get { return m_runningSnapTypes; }
            set { m_runningSnapTypes = value; }
        }
        public bool RunningSnapsEnabled
        {
            get { return m_runningSnaps; }
            set { m_runningSnaps = value; }
        }

        private Graphics currDrawGraphics;
        
        System.Drawing.Drawing2D.SmoothingMode m_smoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed
            | System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

        void UpdateCursor()
        {
            Cursor = m_cursors.GetCursor(m_commandType);
        }

        #region ICanvas 成员

        public IModel DataModel
        {
            get { return m_model; }
            set { m_model = value; }
        }

        public UnitPoint ScreenTopLeftToUnitPoint()
        {
            return ToUnit(new PointF(0, 0));
        }

        public UnitPoint ScreenBottomRightToUnitPoint()
        {
            return ToUnit(new PointF(this.ClientRectangle.Width, this.ClientRectangle.Height));
        }

        public void SetDefaultCenter()
        {
            UnitPoint p1 = ScreenTopLeftToUnitPoint();
            this.SetCenter(p1);
        }

        public PointF ToScreen(UnitPoint unitpoint)
        {
            PointF transformedPoint = unitpoint.Point;
            transformedPoint.Y = transformedPoint.Y;
            transformedPoint.Y *= m_screenResolution * zoom;
            transformedPoint.X *= m_screenResolution * zoom;

            transformedPoint.X += m_panOffset.X + m_dragOffset.X;
            transformedPoint.Y += m_panOffset.Y + m_dragOffset.Y;
            return transformedPoint;
        }

        public float ToScreen(double unitvalue)
        {
            return (float)(unitvalue * m_screenResolution * zoom);
        }

        public double ToUnit(float screenvalue)
        {
            return (double)screenvalue / (double)(m_screenResolution * zoom);
        }

        public UnitPoint ToUnit(PointF screenpoint)
        {
            float panoffsetX = m_panOffset.X + m_dragOffset.X;
            float panoffsetY = m_panOffset.Y + m_dragOffset.Y;
            float xpos = (screenpoint.X - panoffsetX) / (m_screenResolution * zoom);
            float ypos = ((screenpoint.Y - panoffsetY)) / (m_screenResolution * zoom);
            // float ypos = ScreenHeight() - ((screenpoint.Y - panoffsetY)) / (m_screenResolution * m_model.Zoom);
            return new UnitPoint(xpos, ypos);
        }

        public IDrawObject CurrentObject
        {
            get { return m_newObject; }
        }

       

        public Pen CreatePen(Color color, float unitWidth)
        {
            return GetPen(color, ToScreen(unitWidth));
        }
        Graphics m_graphics;
        protected override void OnPaint(PaintEventArgs e)
        {
          
            ClearPens();
            if (m_model == null) return;
            e.Graphics.SmoothingMode = m_smoothingMode;
            Rectangle cliprectangle = e.ClipRectangle;
            if (m_staticImage == null)
            {
                cliprectangle = ClientRectangle;
                m_staticImage = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
                m_staticDirty = true;
            }
            RectangleF r = ToUnitNormalized(cliprectangle);
            if (m_staticDirty)
            {
                m_staticDirty = false;
                currDrawGraphics = Graphics.FromImage(m_staticImage);

                currDrawGraphics.SmoothingMode = m_smoothingMode;
                m_model.BackgroundLayer.Draw(this, r);
              
                if (m_model.GridLayer.Enabled)
                    m_model.GridLayer.Draw(this, r);

                //绘制原点坐标
                PointF nullPoint = ToScreen(new UnitPoint(0, 0));
                currDrawGraphics.DrawLine(Pens.Blue, nullPoint.X - 10, nullPoint.Y, nullPoint.X + 10, nullPoint.Y);
                currDrawGraphics.DrawLine(Pens.Blue, nullPoint.X, nullPoint.Y - 10, nullPoint.X, nullPoint.Y + 10);

                ICanvasLayer[] layers = m_model.Layers;
                for (int layerindex = layers.Length - 1; layerindex >= 0; layerindex--)
                {
                    if (layers[layerindex] != m_model.ActiveLayer && layers[layerindex].Visible)
                        layers[layerindex].Draw(this, r);
                }
                if (m_model.ActiveLayer != null)
                    m_model.ActiveLayer.Draw(this, r);

                this.Dispose();
            }
            base.OnPaint(e);
        }

        public RectangleF ToUnitNormalized(Rectangle screenrect)
        {
            UnitPoint point = ToUnit(screenrect.Location);
            SizeF size = new SizeF((float)this.ToUnit(screenrect.Width), (float)this.ToUnit(screenrect.Height));
            RectangleF unitrect = new RectangleF(point.Point, size);
            return unitrect;
        }

        #region 以下函数只应在OnPoint中执行
        public void DrawLine(Pen pen, UnitPoint p1, UnitPoint p2)
        {
            PointF tmpp1 = ToScreen(p1);
            PointF tmpp2 = ToScreen(p2);
            currDrawGraphics.DrawLine(pen, tmpp1, tmpp2);
        }

        public void DrawArc( Pen pen, UnitPoint center, float radius, float startAngle, float sweepAngle)
        {
            PointF p1 = ToScreen(center);
            radius = (float)Math.Round(ToScreen(radius));
            RectangleF r = new RectangleF(p1, new SizeF());
            r.Inflate(radius, radius);
            if (radius > 0 && radius < 1e8f)
                currDrawGraphics.DrawArc(pen, r, -startAngle, -sweepAngle);
        }
        public void DrawCircle(Pen pen, UnitPoint center, float radius)
        {
            PointF p1 = ToScreen(center);
            radius = (float)Math.Round(ToScreen(radius));
            RectangleF r = new RectangleF(p1, new SizeF());
            r.Inflate(radius, radius);
            currDrawGraphics.DrawEllipse(pen, r);
        }

        public void DrawEllipse( Pen pen, UnitPoint center, float xRadius, float yRadius)
        {
            PointF p1 = ToScreen(center);
            xRadius = (float)Math.Round(ToScreen(xRadius));
            yRadius = (float)Math.Round(ToScreen(yRadius));
            RectangleF r = new RectangleF(p1, new SizeF());
            r.Inflate(xRadius, yRadius);
            currDrawGraphics.DrawEllipse(pen, r);
        }

        public void DrawLinePath(Pen pen, UnitPoint[] points)
        {
            GraphicsPath path = new GraphicsPath();
            for (int i = 0; i < points.Length - 1; i++)
            {
                PointF p1 = ToScreen(points[i]);
                PointF p2 = ToScreen(points[i + 1]);
                path.AddLine(p1, p2);
            }
            currDrawGraphics.DrawPath(pen, path);
        }

        public void DrawRectangle( Pen pen, UnitPoint center, float xRadius, float yRadius)
        {
            PointF p1 = ToScreen(center);
            xRadius = (float)Math.Round(ToScreen(xRadius));
            yRadius = (float)Math.Round(ToScreen(yRadius));
            RectangleF r = new RectangleF(p1, new SizeF());
            r.Inflate(xRadius, yRadius);
            currDrawGraphics.DrawRectangle(pen, r.Left, r.Top, r.Width, r.Height);
            // canvas.Graphics.DrawRectangle(pen, r);
        }

        public void FillEllipse( Brush brush, UnitPoint center, float xRadius, float yRadius)
        {
            PointF p1 = ToScreen(center);
            xRadius = (float)Math.Round(ToScreen(xRadius));
            yRadius = (float)Math.Round(ToScreen(yRadius));
            RectangleF r = new RectangleF(p1, new SizeF());
            r.Inflate(xRadius, yRadius);
            currDrawGraphics.FillEllipse(brush, r);

        }

        public void FillCircle(Brush brush, UnitPoint center, float radius)
        {
            PointF p1 = ToScreen(center);
            radius = (float)Math.Round(ToScreen(radius));
            RectangleF r = new RectangleF(p1, new SizeF());
            r.Inflate(radius, radius);

            currDrawGraphics.FillEllipse(brush, r);
        }
        #endregion
        #endregion


        Dictionary<float, Dictionary<Color, Pen>> m_penCache = new Dictionary<float, Dictionary<Color, Pen>>();
        Pen GetPen(Color color, float width)
        {
            if (m_penCache.ContainsKey(width) == false)
                m_penCache[width] = new Dictionary<Color, Pen>();
            if (m_penCache[width].ContainsKey(color) == false)
                m_penCache[width][color] = new Pen(color, width);
            return m_penCache[width][color];
        }
        void ClearPens()
        {
            m_penCache.Clear();
        }

        public void SetCenter(UnitPoint unitPoint)
        {
            PointF point = ToScreen(unitPoint);
            m_lastCenterPoint = unitPoint;
            SetCenterScreen(point, false);
        }
        public void SetCenter()
        {
            Point point = this.PointToClient(Control.MousePosition);
            SetCenterScreen(point, true);
        }
        public UnitPoint GetCenter()
        {
            return ToUnit(new PointF(this.ClientRectangle.Width / 2, this.ClientRectangle.Height / 2));
        }
        protected void SetCenterScreen(PointF screenPoint, bool setCursor)
        {
            float centerX = ClientRectangle.Width / 2;
            m_panOffset.X += centerX - screenPoint.X;

            float centerY = ClientRectangle.Height / 2;
            m_panOffset.Y += centerY - screenPoint.Y;

            if (setCursor)
                Cursor.Position = this.PointToScreen(new Point((int)centerX, (int)centerY));
            DoInvalidate(true);
        }
        public void DoInvalidate(bool dostatic, RectangleF rect)
        {
            if (dostatic)
                m_staticDirty = true;
            Invalidate(ScreenUtils.ConvertRect(rect));
        }
        public void DoInvalidate(bool dostatic)
        {
            if (dostatic)
                m_staticDirty = true;
            Invalidate();
        }

        #region ICanvas 成员


        public void DrawText(Brush brush, UnitPoint location, StringAlignment alignent, float height,float angle)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region ICanvas 成员


        public Graphics Graphics
        {
            get { return currDrawGraphics; }
        }

        #endregion

        #region ICanvas 成员


        public bool IsPointInLine(UnitPoint m_p1, UnitPoint m_p2, UnitPoint point, float thWidth)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
